package cn.org.july.weixin.common.exception;



import org.springframework.lang.Nullable;

import java.util.Formatter;

/**
 * 自定义异常
 *
 * @auther: wanghongjie
 * @blame: wanghongjie
 * @date: 2020-08-30 16:45
 * @Description:
 */
public class WxException extends RuntimeException {
    /**
     * 消息参数
     */
    private Object[] args;

    /**
     * 异常码
     */
    private IStatus errorCode;

    /**
     * 所属模块
     */
    private WxModuleEnum module;


    /**
     * 构造一个sdk 异常
     *
     * @param module    所属模块
     * @param errorCode 异常枚举
     * @param cause     异常原因
     * @param args      消息参数
     */
    private WxException(WxModuleEnum module, IStatus errorCode, @Nullable Object[] args, @Nullable Throwable cause) {
        super(formatMessage(module, errorCode, args), cause);
        this.errorCode = errorCode == ErrorCode.THROW ? new ThrowCodeHolder(args) : errorCode;
        this.module = module;
        this.args = args;
    }

    /**
     * 获取 异常编码
     *
     * @return 编码对象
     */
    public IStatus getErrorCode() {
        return errorCode;
    }

    /**
     * 获取当前模块
     *
     * @return
     */
    public WxModuleEnum getModule() {
        return module;
    }

    /**
     * 获取 8 位长度的 full code
     *
     * @return 8位code
     */
    public String getFullErrorCode() {
        return String.format("%s%s", module.moduleCode(), errorCode.errorCode());
    }

    /**
     * 获取异常参数
     *
     * @return 参数
     */
    public Object[] getArgs() {
        return args;
    }


    /**
     * 生成一个 sdk 异常对象
     *
     * @param module    所属模块
     * @param errorCode 异常 code
     * @param args      异常 参数
     * @return 异常对象
     */
    public static WxException of(WxModuleEnum module, IStatus errorCode, @Nullable Object... args) {
        assert null != errorCode;
        return new WxException(module, errorCode, args, null);
    }

    /**
     * 生成一个 sdk 异常对象
     *
     * @param module    所属模块
     * @param errorCode 异常 code
     * @param args      异常 参数
     * @param cause     异常源
     * @return 异常对象
     */
    public static WxException of(WxModuleEnum module, IStatus errorCode, @Nullable Object[] args, Throwable cause) {
        assert null != errorCode;
        assert null != module;

        return new WxException(module, errorCode, args, cause == CAUSE_NOOP ? null : cause);
    }

    /**
     * 生成一个 sdk 异常对象
     *
     * @param module    所属模块
     * @param errorCode 异常 code
     * @param cause     异常源
     * @param args      异常 参数
     * @return 异常对象
     */
    public static WxException of(WxModuleEnum module, IStatus errorCode, Throwable cause, Object... args) {
        assert null != errorCode;
        assert null != module;
        return new WxException(module, errorCode, args, cause == CAUSE_NOOP ? null : cause);
    }

    /**
     * 通过 code  + message 生成，用于底层穿透场景
     *
     * @param code    异常码
     * @param message 消息
     * @return sdk ex
     */
    public static WxException of(WxModuleEnum module, String code, String message) {
        assert null != code;
        assert null != module;
        return new WxException(module, new ThrowCodeHolder(code, message), null, null);
    }

    /**
     * 格式化消息
     *
     * @return
     */
    private static String formatMessage(WxModuleEnum module, IStatus errorCode, Object[] args) {
        return errorCode == ErrorCode.THROW ? String.format("%s%s", args[0], args[1]) : module.moduleName() + ":" + new Formatter().format(errorCode.getMessage(), args).toString();
    }

    /**
     * 空异常，用于cause == null 时参数占位
     */
    public static final Throwable CAUSE_NOOP = new RuntimeException();
}
